package com.s7.widget.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;

import androidx.appcompat.widget.AppCompatImageView;

import com.s7.widget.R;

public class RoundImageView extends AppCompatImageView {

    private boolean isOval;

    private int mBorderColor;
    private float mBorderWidth = 0.0f;
    private float mCornerRadius = 0.0f;

    private float mLeftTopRadius = 0.0f;
    private float mRightTopRadius = 0.0f;
    private float mLeftBottomRadius = 0.0f;
    private float mRightBottomRadius = 0.0f;

    private Paint mBitmapPaint;

    private Paint mBorderPaint;

    private float mRadius;

    private int mMaskColor;

    private Paint mMaskPaint;

    private Matrix mMatrix;

    private BitmapShader mBitmapShader;

    private int mWidth, mHeight;

    private RectF mRoundRect;
    private Path mRoundPath;

    public RoundImageView(Context context) {
        this(context, null);
    }

    public RoundImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView, defStyleAttr, 0);

        isOval = a.getBoolean(R.styleable.RoundImageView_roundOval, false);
        mBorderColor = a.getColor(R.styleable.RoundImageView_roundBorderColor, Color.WHITE);
        mBorderWidth = a.getDimension(R.styleable.RoundImageView_roundBorderWidth, 0.0f);
        mCornerRadius = a.getDimension(R.styleable.RoundImageView_roundCornerRadius, 0.0f);
        mLeftTopRadius = a.getDimension(R.styleable.RoundImageView_roundLeftTopCornerRadius, 0.0f);
        mRightTopRadius = a.getDimension(R.styleable.RoundImageView_roundRightTopCornerRadius, 0.0f);
        mLeftBottomRadius = a.getDimension(R.styleable.RoundImageView_roundLeftBottomCornerRadius, 0.0f);
        mRightBottomRadius = a.getDimension(R.styleable.RoundImageView_roundRightBottomCornerRadius, 0.0f);
        mMaskColor = a.getColor( R.styleable.RoundImageView_roundMaskColor, Color.TRANSPARENT);
        a.recycle();
        init();

    }

    private void init() {
        mRoundPath = new Path();
        mMatrix = new Matrix();
        mBitmapPaint = new Paint();
        mBitmapPaint.setAntiAlias(true);
        mBorderPaint = new Paint();
        mBorderPaint.setAntiAlias(true);
        mBorderPaint.setStyle(Paint.Style.STROKE);
        mBorderPaint.setStrokeCap(Paint.Cap.ROUND);
        mMaskPaint = new Paint();
        mMaskPaint.setAntiAlias(true);
        mMaskPaint.setStyle(Paint.Style.FILL);
        mMaskPaint.setXfermode(null);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        mWidth = widthMeasureSpec;
        mHeight = heightMeasureSpec;

        if (isOval) {
            mRadius = Math.min(MeasureSpec.getSize(mWidth),
                    MeasureSpec.getSize(mHeight)) / 2 - mBorderWidth / 2;
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        if (!(isOval && w == h)) {
            mRoundRect = new RectF(mBorderWidth / 2, mBorderWidth / 2,
                    w - mBorderWidth / 2, h - mBorderWidth / 2);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {

        mBorderPaint.setColor(mBorderColor);
        mBorderPaint.setStrokeWidth(mBorderWidth);

        mMaskPaint.setColor(mMaskColor);

        if (getDrawable() == null) {
            return;
        }
        setUpShader();

        if (isOval) {
            if (getWidth() == getHeight()) {
                canvas.drawCircle(mRadius + mBorderWidth / 2, mRadius + mBorderWidth / 2, mRadius, mBitmapPaint);
                if (mMaskColor != Color.TRANSPARENT) {
                    canvas.drawCircle(mRadius + mBorderWidth / 2, mRadius + mBorderWidth / 2, mRadius, mMaskPaint);
                }
                canvas.drawCircle(mRadius + mBorderWidth / 2, mRadius + mBorderWidth / 2, mRadius, mBorderPaint);

            } else {
                canvas.drawOval(mRoundRect, mBitmapPaint);
                if (mMaskColor != Color.TRANSPARENT) {
                    canvas.drawOval(mRoundRect, mMaskPaint);
                }
                canvas.drawOval(mRoundRect, mBorderPaint);
            }
        } else {
            setRoundPath();
            canvas.drawPath(mRoundPath, mBitmapPaint);
            if (mMaskColor != Color.TRANSPARENT) {
                canvas.drawPath(mRoundPath, mMaskPaint);
            }
            canvas.drawPath(mRoundPath, mBorderPaint);
        }
    }


    private void setRoundPath() {
        mRoundPath.reset();

        if (mCornerRadius > 0) {
            mRoundPath.addRoundRect(mRoundRect,
                    new float[]{mCornerRadius, mCornerRadius,
                            mCornerRadius, mCornerRadius,
                            mCornerRadius, mCornerRadius,
                            mCornerRadius, mCornerRadius},
                    Path.Direction.CW);
        } else {
            mRoundPath.addRoundRect(mRoundRect,
                    new float[]{mLeftTopRadius, mLeftTopRadius,
                            mRightTopRadius, mRightTopRadius,
                            mRightBottomRadius, mRightBottomRadius,
                            mLeftBottomRadius, mLeftBottomRadius},
                    Path.Direction.CW);
        }

    }

    private void setUpShader() {
        Drawable drawable = getDrawable();
        if (drawable == null) {
            return;
        }

        Bitmap bmp = drawableToBitamp(drawable);
        mBitmapShader = new BitmapShader(bmp, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        float scale = 1.0f;

        if (!(bmp.getWidth() == getWidth() && bmp.getHeight() == getHeight())) {

            scale = Math.max(getWidth() * 1.0f / bmp.getWidth(),
                    getHeight() * 1.0f / bmp.getHeight());
            float dx = (scale * bmp.getWidth() - getWidth()) / 2;
            float dy = (scale * bmp.getHeight() - getHeight()) / 2;
            mMatrix.setTranslate(-dx, -dy);
        }
        mMatrix.preScale(scale, scale);
        mBitmapShader.setLocalMatrix(mMatrix);

        mBitmapShader.setLocalMatrix(mMatrix);
        mBitmapPaint.setShader(mBitmapShader);
    }

    private Bitmap drawableToBitamp(Drawable drawable) {
        try {
            Bitmap bitmap;
            if (drawable instanceof BitmapDrawable) {
                BitmapDrawable bd = (BitmapDrawable) drawable;
                return bd.getBitmap();
            }
            int w = drawable.getIntrinsicWidth();
            int h = drawable.getIntrinsicHeight();
            bitmap = Bitmap.createBitmap(w, h, drawable.getOpacity() != PixelFormat.OPAQUE
                    ? Bitmap.Config.ARGB_4444 : Bitmap.Config.RGB_565);
            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, w, h);
            drawable.draw(canvas);
            return bitmap;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public RoundImageView setOval(boolean oval) {
        if (this.isOval != oval) {
            this.isOval = oval;
            requestLayout();
        }
        return this;
    }

    public RoundImageView setCornerRadius(int cornerRadius) {
        cornerRadius = dp2px(cornerRadius);
        if (mCornerRadius != cornerRadius) {
            mCornerRadius = cornerRadius;
            invalidate();
        }
        return this;
    }

    public RoundImageView setCornerRadius(int mLeftTopRadius, int mRightTopRadius,
                                          int mLeftBottomRadius, int mRightBottomRadius) {
        if (this.mLeftTopRadius != dp2px(mLeftTopRadius) || this.mRightTopRadius != dp2px(mRightTopRadius)
                || this.mLeftBottomRadius != dp2px(mLeftBottomRadius)
                || this.mRightBottomRadius != dp2px(mRightBottomRadius)) {
            this.mLeftTopRadius = dp2px(mLeftTopRadius);
            this.mRightTopRadius = dp2px(mRightTopRadius);
            this.mLeftBottomRadius = dp2px(mLeftBottomRadius);
            this.mRightBottomRadius = dp2px(mRightBottomRadius);
            invalidate();
        }
        return this;
    }

    public RoundImageView setBorderWidth(int borderWidth) {
        borderWidth = dp2px(borderWidth);
        if (mBorderWidth != borderWidth) {
            mBorderWidth = borderWidth;
            invalidate();
        }
        return this;
    }

    public RoundImageView setBorderColor(int borderColor) {
        if (mBorderColor != borderColor) {
            mBorderColor = borderColor;
            invalidate();
        }
        return this;
    }

    public RoundImageView setMaskColor(int mMaskColor) {
        if (mMaskColor != mMaskColor) {
            mBorderColor = mMaskColor;
            invalidate();
        }
        return this;
    }

    private int dp2px(int dpVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dpVal, getResources().getDisplayMetrics());
    }

}